#!/bin/sh
set -eu

name="fetchpijul"
remote=
channel="main"
change=
state=
date=
exp_hash=

usage() {
	echo "Usage: nix-prefetch-pijul [options] [REMOTE] [STATE [EXPECTED-HASH]]"
	echo
	echo "Options:"
	echo "	--name <NAME>        Symbolic store path name to use for the result."
	echo "	--remote <REMOTE>    URL for the Pijul repository."
	echo "	--change <CHANGE>    Clone a specific change."
	echo "	--state <STATE>      Clone a specific state."
	echo "	--channel <CHANNEL>  Channel name (default: ‘main’)."
	echo "	--hash <HASH>        Expected hash."
	echo "	--help               Show this help message."
}

# Argument parsing
while [ $# -gt 0 ]; do
	case "$1" in
		--name)
			name="$2"; shift 2 ;;
		--remote)
			remote="$2"; shift 2 ;;
		--channel)
			channel="$2"; shift 2 ;;
		--change)
			change="$2"; shift 2 ;;
		--state)
			state="$2"; shift 2 ;;
		--hash)
			exp_hash="$2"; shift 2 ;;
		--help)
			usage; exit 0 ;;
		*)
			# Positional arguments
			if [ -z "$remote" ]; then
				remote="$1"
				shift
			elif [ -z "$state" ]; then
				state="$1"
				shift
			elif [ -z "$exp_hash" ]; then
				exp_hash="$1"
				shift
			else
				echo "Error: Too many arguments" >&2
				usage
				exit 1
			fi
			;;
	esac
done

if [ -z "$remote" ]; then
	echo "Error: URL for remote is required." >&2
	echo >&2
	usage
	exit 1
elif [ -n "$change" -a -n "$state" ]; then
	echo "Error: Only one of ‘change’ or ‘state’ can be set" >&2
	echo >&2
	usage
	exit 1
fi

hash=
hash_algo="${NIX_HASH_ALGO:-"sha256"}"
hash_format="${hashFormat:-"--base32"}"
final_path=

# If the hash was given, a file with that hash may already be in the
# store.
if [ -n "$exp_hash" ]; then
	final_path=$(nix-store --print-fixed-path --recursive "$hash_algo" "$exp_hash" "$name")
	if ! nix-store --check-validity "$final_path" 2> /dev/null; then
		final_path=""
	fi
	hash="$exp_hash"
fi

# If we don’t know the hash or a path with that hash doesn’t exist,
# download the file and add it to the store.
if [ -z "$final_path" ]; then
	tmp_clone="$(realpath "$(mktemp -d --tmpdir pijul-clone-tmp-XXXXXXXX)")"
	trap "rm -rf \"$tmp_clone\"" EXIT

	clone_args="--channel $channel"
	if [ -n "$change" ]; then
		clone_args="$clone_args --change $change"
	elif [ -n "$state" ]; then
		clone_args="$clone_args --state $state"
	fi

	cd "$tmp_clone"
	pijul clone $clone_args "$remote" "$name"
	latest_log="$(pijul log --repository "$tmp_clone/$name" --limit 1)"
	# State is a stable reference is a stable reference for the patchset that
	# doesn't depend on patch order. As a result, it will always be included.
	if [ -z "$state" ]; then
		state="$(printf "%s" "$latest_log" | awk '/^State:/ {printf $2}')"
	fi
	date="$(printf "%s" "$latest_log" | awk '/^Date:/ { sub(/^Date:[[:space:]]*/, "", $0); printf $0 }')"
	date="$(date -u -d "$date" "+%Y-%m-%dT%H:%M:%SZ")"

	rm -rf "$tmp_clone/$name/.pijul"

	hash="$(nix-hash --type "$hash_algo" "$hash_format" "$tmp_clone/$name")"
	final_path=$(nix-store --add-fixed --recursive "$hash_algo" "$tmp_clone/$name")

	if [ -n "$exp_hash" -a "$exp_hash" != "$hash" ]; then
		echo "Hash mismatch for “$remote” @ “$channel”" >&2
		echo "Expected: $exp_hash" >&2
		echo "Got:      $hash" >&2
		exit 1
	fi
fi

json_escape() {
	printf '%s' "$1" | jq -Rs .
}

cat <<EOF
{
	"remote": $(json_escape "$remote"),
	"channel": $(json_escape "$channel"),
EOF
if [ -n "$change" ]; then cat <<EOF
	"change": $(json_escape "$change"),
EOF
fi; cat <<EOF
	"state": $(json_escape "$state"),
	"date": $(json_escape "$date"),
	"path": "$final_path",
	$(json_escape "$hash_algo"): $(json_escape "$hash"),
	"hash": "$(nix-hash --to-sri --type "$hash_algo" "$hash")"
}
EOF
# vim: noet ci pi sts=0
